package com.omuao.common.utils.verification.impl;

import com.omuao.common.exception.MessageCodeException;
import com.omuao.common.utils.verification.Verification;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 验证工具实现类
 *
 * @author yumi@omuao.com
 * @since 2019-10-15
 **/
public class VerificationImpl<T> implements Verification<T, MessageCodeException> {
    /**
     * 即时返回异常
     */
    private boolean immediateException = false;

    /**
     * 结果
     */
    private boolean result = false;

    /**
     * 消息
     */
    private String message;

    /**
     * 源数据对象
     */
    private Object source;

    /**
     * 日期格式化规则
     */
    private String pattern;
    /**
     * 比较格式化规则
     */
    private String comparePattern;


    public void init() {
        pattern = "yyyy-MM-dd HH:mm:ss";
        comparePattern = "yyyy-MM-dd HH:mm:ss";
        result = true;
    }

    public VerificationImpl() {
        init();
    }

    public VerificationImpl(boolean immediateException) {
        this.immediateException = immediateException;
        init();
    }

    @Override
    public Verification message(String message) throws MessageCodeException {
        this.message = message;
        return this;
    }

    @Override
    public Verification immediateException(boolean flag) throws MessageCodeException {
        this.immediateException = flag;
        return this;
    }

    @Override
    public boolean result() throws MessageCodeException {
        return this.result;
    }

    @Override
    public Verification pattern(String pattern) throws MessageCodeException {
        this.pattern = pattern;
        return this;
    }

    @Override
    public Verification comparePattern(String pattern) throws MessageCodeException {
        this.comparePattern = pattern;
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> source(T source) throws MessageCodeException {
        this.setSource(source);
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> notNull() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                System.out.println(immediateException);
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isEmpty() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Collection.class.isAssignableFrom(source.getClass())) {
                result = false;
                throw new MessageCodeException(message);
            } else {
                Collection collection = (Collection) source;
                if (!collection.isEmpty()) {
                    result = false;
                    if (immediateException) {
                        throw new MessageCodeException(message);
                    }
                }
            }

        }

        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isNum() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Number.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isDouble() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Double.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isInteger() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Integer.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isLong() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Long.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isShort() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Short.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isByte() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Byte.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isFloat() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Float.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isCharacter() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Character.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isBoolean() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Boolean.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isNull() throws MessageCodeException {
        if (source != null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isString() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!String.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isEmptyString() throws MessageCodeException {
        this.isString();
        if (result) {
            String s = (String) source;

            if (s.length() != 0) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }


        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isEmptyStringWithTrim() throws MessageCodeException {
        this.isString();
        if (result) {
            String s = (String) source;

            if (s.trim().length() != 0) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isArray() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Object.class.isArray()) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isArrayEmpty() throws MessageCodeException {
        this.isArray();
        if (result) {
            Object[] array = (Object[]) source;
            if (array.length != 0) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isList() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!List.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isMap() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Map.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isSet() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Set.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isCollection() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Collection.class.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isClass() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!(source instanceof Class)) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isDestClass(Class dest) throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!dest.isAssignableFrom(source.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isPrimaryClass() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!source.getClass().isPrimitive()) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isInterface() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!source.getClass().isInterface()) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isDestSubInterface(Class dest) throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!(dest.isInterface() && dest.isAssignableFrom(source.getClass()))) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }

    @Override
    public Verification<T, MessageCodeException> isDate() throws MessageCodeException {
        if (source == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (String.class.isAssignableFrom(source.getClass())) {
                SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
                try {
                    dateFormat.parse((String) source);
                } catch (ParseException e) {
                    result = false;
                    if (immediateException) {
                        throw new MessageCodeException(message);
                    }
                }
            } else {
                if (!Date.class.isAssignableFrom(source.getClass())) {
                    result = false;
                    if (immediateException) {
                        throw new MessageCodeException(message);
                    }
                }
            }
        }
        return this;
    }

    @Override
    public <Compare> Verification<T, MessageCodeException> eq(Compare compare) throws MessageCodeException {
        Object sourceTemp = this.source;

        Object compareTemp = compare;

        if (sourceTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (compareTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (!Comparable.class.isAssignableFrom(sourceTemp.getClass()) && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        Comparable comparable = (Comparable) sourceTemp;
        result = comparable.compareTo(compareTemp) != 0;

        if (!result) {
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        return this;
    }

    @Override
    public <Compare> Verification<T, MessageCodeException> gt(Compare compare) throws MessageCodeException {
        Object sourceTemp = this.source;

        Object compareTemp = compare;

        if (sourceTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (compareTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (!Comparable.class.isAssignableFrom(sourceTemp.getClass()) && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        Comparable comparable = (Comparable) sourceTemp;
        result = comparable.compareTo(compareTemp) > 0;

        if (!result) {
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare> Verification<T, MessageCodeException> lt(Compare compare) throws MessageCodeException {
        Object sourceTemp = this.source;

        Object compareTemp = compare;

        if (sourceTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (compareTemp == null && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        if (!Comparable.class.isAssignableFrom(sourceTemp.getClass()) && result) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }

        Comparable comparable = (Comparable) sourceTemp;
        result = comparable.compareTo(compareTemp) < 0;

        if (!result) {
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }


    @Override
    public <Compare> Verification<T, MessageCodeException> exist(Compare compare) throws MessageCodeException {
        if (compare == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Collection.class.isAssignableFrom(compare.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        if (result) {

            Collection collection = (Collection) compare;

            if (!collection.contains(source)) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        return this;
    }

    @Override
    public <Compare> Verification<T, MessageCodeException> notExist(Compare compare) throws MessageCodeException {
        if (compare == null) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        } else {
            if (!Collection.class.isAssignableFrom(compare.getClass())) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        if (result) {

            Collection collection = (Collection) compare;

            if (collection.contains(source)) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        return this;
    }


    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateBefore(Compare compare) throws MessageCodeException {
        this.isDate();

        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        }
        if (result && (!((Date) source).before(compareDate))) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateAfter(Compare compare) throws MessageCodeException {
        this.isDate();

        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }

        if (result && (!((Date) source).after(compareDate))) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateRange(Compare compare, Date date) throws MessageCodeException {
        this.isDate();
        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        if (!(sourceDate.before(date) && compareDate.after(date))) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateNotRange(Compare compare, Date date) throws MessageCodeException {
        this.isDate();

        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        if (sourceDate.before(date) && compareDate.after(date)) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateRange(Compare compare, Date date, int offset) throws MessageCodeException {
        this.isDate();
        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, offset);

        Date dateTemp = calendar.getTime();
        if (!(sourceDate.before(dateTemp) && compareDate.after(dateTemp))) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateNotRange(Compare compare, Date date, int offset) throws MessageCodeException {
        this.isDate();
        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, offset);

        Date dateTemp = calendar.getTime();
        if (sourceDate.before(dateTemp) && compareDate.after(dateTemp)) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateRange(Compare compare, Date date, int offset, int unit) throws MessageCodeException {
        this.isDate();
        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(unit, offset);

        Date dateTemp = calendar.getTime();
        if (!(sourceDate.before(dateTemp) && compareDate.after(dateTemp))) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    @Override
    public <Compare extends Date> Verification<T, MessageCodeException> dateNotRange(Compare compare, Date date, int offset, int unit) throws MessageCodeException {
        this.isDate();
        Date compareDate = null;
        if (String.class.isAssignableFrom(compare.getClass()) && result) {

            SimpleDateFormat dateFormat = new SimpleDateFormat(comparePattern);

            try {
                compareDate = dateFormat.parse(String.valueOf(compare));
            } catch (ParseException e) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }

        } else {
            if ((!Date.class.isAssignableFrom(compare.getClass())) && result) {
                result = false;
                if (immediateException) {
                    throw new MessageCodeException(message);
                }
            }
        }
        Date sourceDate = (Date) source;

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(unit, offset);

        Date dateTemp = calendar.getTime();
        if (sourceDate.before(dateTemp) && compareDate.after(dateTemp)) {
            result = false;
            if (immediateException) {
                throw new MessageCodeException(message);
            }
        }
        return this;
    }

    public boolean isImmediateException() {
        return immediateException;
    }

    public void setImmediateException(boolean immediateException) {
        this.immediateException = immediateException;
    }

    public boolean isResult() {
        return result;
    }

    public void setResult(boolean result) {
        this.result = result;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getSource() {
        return source;
    }

    public void setSource(Object source) {
        this.source = source;
    }

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
}
